package com.changgou.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.entity.Result;
import com.changgou.goods.pojo.Sku;
import com.changgou.search.dao.SkuMapper;
import com.changgou.search.feign.SkuFeign;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SkuService;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.ScoredPage;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @Author: taku
 * @CreateTime: 2020-05-10 17:11
 * @Description: SkuService实现类
 */
@Service
public class SkuServiceImpl implements SkuService {

    @Autowired
    private SkuFeign skuFeign;

    @Autowired
    private SkuMapper skuMapper;

    @Autowired
    private ElasticsearchTemplate EsTemplate;

    /**
     * 将mysql的数据导入ES数据库
     */
    @Override
    public void importToEs() {
        // 获取mysql数据
        Result<List<Sku>> byStatus = skuFeign.findByStatus("1");
        List<Sku> data = byStatus.getData();
        // 转为list<SkuInfo>
        List<SkuInfo> skuInfos = JSON.parseArray(JSON.toJSONString(data), SkuInfo.class);
//        // 添加关键字查询需要的数据Map
        for (SkuInfo skuInfo : skuInfos) {
            String spec = skuInfo.getSpec();
            Map<String, Object> map = JSON.parseObject(spec);
            skuInfo.setSpecMap(map);
        }
        // 写入ES数据库
        skuMapper.saveAll(skuInfos);

    }

    /**
     * 搜索
     *
     * @param searchMap
     * @return
     */
    @Override
    public Map search(Map<String, String> searchMap) {
        // 获取关键字
        String keyword = searchMap.get("keyword");
        if (StringUtils.isBlank(keyword)) {
            keyword = "华为";
        }
        // 创建查询条件

        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchQuery("name", keyword));

        // 设置分类分组
        builder.addAggregation(AggregationBuilders.terms("skuCategoryGroup").field("categoryName").size(50));
        // 设置品牌分组
        builder.addAggregation(AggregationBuilders.terms("skuBrandGroup").field("brandName").size(50));
        // 设置规格分组
        builder.addAggregation(AggregationBuilders.terms("skuSpecGroup").field("spec.keyword").size(50));

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // 添加条件筛选 1分类,2 品牌
        String brand = searchMap.get("brand");
        if (StringUtils.isNotBlank(brand)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brandName", brand));
        }
        String category = searchMap.get("category");
        if (StringUtils.isNotBlank(category)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("categoryName", category));
        }

        // 添加规格筛选 specMap.网络.keyword....specMap.颜色.keyword 规格传入格式spec_规格
        // 遍历searchMap查找规格值
        if (searchMap != null) {
            for (String key : searchMap.keySet()) {
                if (key.startsWith("spec_")) {
                    boolQueryBuilder.filter(QueryBuilders.termQuery("specMap." + key.substring(5) + ".keyword", searchMap.get(key)));
                }
            }
        }

        // 添加价格筛选 price: 100-199 或 1000-*(1000以上)
        String price = searchMap.get("price");
        if (StringUtils.isNotBlank(price)) {
            String[] priceArea = price.split("-");
            // 如果是上限数据
            if ("*".equals(priceArea[1])) {
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(priceArea[0]));
            } else {
                // 如果是价格区间设置
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(priceArea[0]).lte(priceArea[1]));
            }
        }

        // 分页设置
        // 从searchMap中获取pageNum,如果设置则默认为第一页,即pageNum = 0;
        int pageNum = 0;
        // 默认显示40条记录
        int size = 40;
        String pageNumStr = searchMap.get("pageNum");
        if (StringUtils.isNotBlank(pageNumStr)) {
            pageNum = Integer.valueOf(pageNumStr) - 1;
        }
        builder.withPageable(PageRequest.of(pageNum,size));

        // 排序检索
        // 获取排序字段和规则sortType,sortRule
        String sortField = searchMap.get("sortField");
        String sortRule = searchMap.get("sortRule");
        if (StringUtils.isNotBlank(sortField) && StringUtils.isNotBlank(sortRule)) {
            builder.withSort(SortBuilders.fieldSort(sortField).order(SortOrder.valueOf(sortRule)));
        }
        // 设置高亮, 1 指定高亮字段field 2 设置高亮标签tags
        builder.withHighlightFields(new HighlightBuilder.Field("name"));
        builder.withHighlightBuilder(new HighlightBuilder().preTags("<em style='color : red;'>").postTags("</em>"));

        builder.withFilter(boolQueryBuilder);
        NativeSearchQuery build = builder.build();
        // 添加高亮前的数据获取 : AggregatedPage<SkuInfo> page = EsTemplate.queryForPage(build, SkuInfo.class);
        AggregatedPage<SkuInfo> page = EsTemplate.queryForPage(build, SkuInfo.class,new MySerachResultMapper());

        // 获取分类分组数据
        StringTerms skuCategoryGroup = (StringTerms) page.getAggregation("skuCategoryGroup");
        ArrayList<String> categoryList = getTermStringsList(skuCategoryGroup);
        // 获取品牌分组数据
        StringTerms skuBrandGroup = (StringTerms) page.getAggregation("skuBrandGroup");
        ArrayList<String> brandList = getTermStringsList(skuBrandGroup);
        // 获取规格数据
        StringTerms skuSpecGroup = (StringTerms) page.getAggregation("skuSpecGroup");
        Map<String, Set<String>> specMap = getTermSpecMap(skuSpecGroup);

        // 获取高亮信息skuList


        HashMap<String, Object> resultMap = new HashMap<>();
        resultMap.put("totalRows", page.getTotalElements());
        resultMap.put("totalPages", page.getTotalPages());
        resultMap.put("skuList", page.getContent());

        resultMap.put("categoryList", categoryList);
        resultMap.put("brandList", brandList);
        resultMap.put("specMap", specMap);
        return resultMap;
    }

    private Map<String, Set<String>> getTermSpecMap(StringTerms skuSpecGroup) {
        HashMap<String, Set<String>> specMap = new HashMap<>();
        List<StringTerms.Bucket> buckets = skuSpecGroup.getBuckets();
        if (buckets != null) {
            for (StringTerms.Bucket bucket : buckets) {
                // 获取每一行的规格转为map
                String specString = bucket.getKeyAsString();
                Map<String, String> map = JSON.parseObject(specString, Map.class);
                for (Map.Entry<String, String> Entry : map.entrySet()) {
                    Set<String> set = specMap.get(Entry.getKey());
                    if (set == null) {
                        set = new HashSet<String>();
                    }
                    // 具体规格值添加值set中
                    set.add(Entry.getValue());
                    specMap.put(Entry.getKey(), set);
                }
            }
        }
        return specMap;
    }

    private ArrayList<String> getTermStringsList(StringTerms group) {
        List<StringTerms.Bucket> buckets = group.getBuckets();
        ArrayList<String> list = new ArrayList<>();
        if (buckets != null) {
            for (StringTerms.Bucket bucket : group.getBuckets()) {
                String keyAsString = bucket.getKeyAsString();
                list.add(keyAsString);
            }
        }
        return list;
    }
}
